Imports Clo = Enhancements.Core.Logging
Imports Ere = Enhancements.Basic.RegularExpressions
Imports Exc = Enhancements.Basic.Expressions.Functions.Conversion
Imports Exe = Enhancements.Basic.Expressions
Imports Exm = Enhancements.Basic.Expressions.Functions.Math
Imports Exl = Enhancements.Basic.Expressions.Functions.Logical
Imports Exi = Enhancements.Basic.Expressions.Functions.TypeInference
Imports Exr = Enhancements.Basic.Expressions.Functions.Relational
Imports Exs = Enhancements.Basic.Expressions.Functions.Strings
Imports Ext = Enhancements.Basic.Expressions.Functions.DateTimes
Imports Fun = Enhancements.Basic.Expressions.Functions
Imports Gen = System.Collections.Generic
Imports Glo = System.Globalization
Imports Iou = System.IO
Imports Ope = Enhancements.Basic.Expressions.Operators
Imports Ref = System.Reflection
Imports Reg = System.Text.RegularExpressions
Imports Sys = System
Imports Tex = System.Text
Imports Val = Enhancements.Basic.Expressions.Values
Imports Xsd = System.Xml.Schema
Imports W3c = System.Xml

Namespace Enhancements.Basic.Expressions
    ''' <summary>Implementation of an expression compiler.</summary>
    <Serializable(), DebuggerStepThrough()> _
    Public NotInheritable Class Compiler

        ''' <summary>Initializes a Compiler instance with the provided functions.</summary>
        ''' <exception cref="CompilerFunctionsAreValidNotException">_functions is invalid.</exception>
        Public Sub New(ByVal _functions() As Fun.Function)
            Functions = _functions
        End Sub

        ''' <summary>Initializes a Compiler instance with the provided functions and other pre-defined, if requested.</summary>
        ''' <exception cref="IsNullException">_additionalFunctions is null.</exception>
        ''' <exception cref="CompilerFunctionsAreValidNotException">The result functions collection is invalid.</exception>
        Public Sub New(ByVal _includeBuiltinConversionFunctions As System.Boolean, ByVal _includeBuiltinMathFunctions As System.Boolean, ByVal _includeBuiltinTypeInferenceFunctions As System.Boolean, ByVal _includeBuiltinLogicalFunctions As System.Boolean, ByVal _includeBuiltinRelationalFunctions As System.Boolean, ByVal _includeBuiltinStringsFunctions As System.Boolean, ByVal _includeBuiltinDateTimesFunctions As System.Boolean, ByVal _additionalFunctions() As Fun.Function)
            _additionalFunctions.IsNullNot("_additionalFunctions", True)

            Dim _functionsList As Gen.List(Of Fun.Function) = New Gen.List(Of Fun.Function)()
            If _includeBuiltinConversionFunctions Then
                _functionsList.Add(New Exc.ToBoolean())
                _functionsList.Add(New Exc.ToDateTime())
                _functionsList.Add(New Exc.ToDecimal())
                _functionsList.Add(New Exc.ToInteger())
                _functionsList.Add(New Exc.ToString())
            End If
            If _includeBuiltinMathFunctions Then
                _functionsList.Add(New Exm.Add())
                _functionsList.Add(New Exm.Divide())
                _functionsList.Add(New Exm.Exponentiate())
                _functionsList.Add(New Exm.Multiply())
                _functionsList.Add(New Exm.Negative())
                _functionsList.Add(New Exm.Positive())
                _functionsList.Add(New Exm.SquareRoot())
                _functionsList.Add(New Exm.Subtract())
                _functionsList.Add(New Exm.Modulus())
            End If
            If _includeBuiltinTypeInferenceFunctions Then
                _functionsList.Add(New Exi.IsBoolean())
                _functionsList.Add(New Exi.IsDateTime())
                _functionsList.Add(New Exi.IsDecimal())
                _functionsList.Add(New Exi.IsInteger())
                _functionsList.Add(New Exi.IsString())
            End If
            If _includeBuiltinLogicalFunctions Then
                _functionsList.Add(New Exl.Complement())
                _functionsList.Add(New Exl.Conjunction())
                _functionsList.Add(New Exl.Disjunction())
            End If
            If _includeBuiltinRelationalFunctions Then
                _functionsList.Add(New Exr.Different())
                _functionsList.Add(New Exr.Equals())
                _functionsList.Add(New Exr.HigherOrEquals())
                _functionsList.Add(New Exr.HigherThan())
                _functionsList.Add(New Exr.LowerOrEquals())
                _functionsList.Add(New Exr.LowerThan())
            End If
            If _includeBuiltinStringsFunctions Then
                _functionsList.Add(New Exs.Contains())
                _functionsList.Add(New Exs.EndsWith())
                _functionsList.Add(New Exs.IndexOf())
                _functionsList.Add(New Exs.LastIndexOf())
                _functionsList.Add(New Exs.StartsWith())
                _functionsList.Add(New Exs.Trim())
                _functionsList.Add(New Exs.TrimEnd())
                _functionsList.Add(New Exs.TrimStart())
                _functionsList.Add(New Exs.Concatenate())
                _functionsList.Add(New Exs.Length())
                _functionsList.Add(New Exs.PadLeft())
                _functionsList.Add(New Exs.PadRight())
                _functionsList.Add(New Exs.Substring())
                _functionsList.Add(New Exs.Left())
                _functionsList.Add(New Exs.Right())
            End If
            If _includeBuiltinDateTimesFunctions Then
                _functionsList.Add(New Ext.Day())
                _functionsList.Add(New Ext.Hour())
                _functionsList.Add(New Ext.Millisecond())
                _functionsList.Add(New Ext.Minute())
                _functionsList.Add(New Ext.Month())
                _functionsList.Add(New Ext.Second())
                _functionsList.Add(New Ext.Year())
                _functionsList.Add(New Ext.DateTimeAdd())
                _functionsList.Add(New Ext.DateTimeSubtract())
            End If
            _functionsList.AddRange(_additionalFunctions)
            Functions = _functionsList.ToArray()
        End Sub

        Private __Functions() As Fun.Function = {}
        ''' <summary>Gets the functions made available to the compiler.</summary>
        Public Property Functions() As Fun.Function()
            Get
                Return __Functions
            End Get
            Private Set(ByVal _value() As Fun.Function)
                Try
                    __FunctionsAnalysis.AssertAreValid(Me, _value)
                Catch _exception As Exception
                    CompilerFunctionsAreValidNotException.___Throw(_value, _exception)
                End Try

                __Functions = _value
            End Set
        End Property

        Private __FunctionsSorted As Gen.Dictionary(Of System.String, Fun.Function) = Nothing
        Friend Function ___FunctionsSortedGet() As Gen.Dictionary(Of System.String, Fun.Function)
            If __FunctionsSorted.IsNull("__FunctionsSorted", False) Then
                Dim _functionsSortedDictionary As Gen.SortedDictionary(Of System.String, Fun.Function) = New Gen.SortedDictionary(Of System.String, Fun.Function)()
                For Each _function As Fun.Function In Functions
                    _functionsSortedDictionary.Add(_function.Name, _function)
                Next _function
                __FunctionsSorted = New Gen.Dictionary(Of System.String, Fun.Function)()
                For _counter As Sys.Int32 = _functionsSortedDictionary.Count - 1 To 0 Step -1
                    __FunctionsSorted.Add(_functionsSortedDictionary.ElementAt(_counter).Key, _functionsSortedDictionary.ElementAt(_counter).Value)
                Next _counter
            End If

            Return __FunctionsSorted
        End Function

        ''' <exception cref="IsNullException"></exception>
        ''' <exception cref="IsListFilledNotException"></exception>
        Friend Function ___FunctionsSortedGet(ByVal _functions() As Fun.Function) As Gen.Dictionary(Of System.String, Fun.Function)
            _functions.IsListFilled("_functions", True)

            Dim _functionsSortedDictionary As Gen.SortedDictionary(Of System.String, Fun.Function) = New Gen.SortedDictionary(Of System.String, Fun.Function)()
            For Each _function As Fun.Function In _functions
                _functionsSortedDictionary.Add(_function.Name, _function)
            Next _function
            Dim _returnValue As Gen.Dictionary(Of System.String, Fun.Function) = New Gen.Dictionary(Of System.String, Fun.Function)()
            For _counter As Sys.Int32 = _functionsSortedDictionary.Count - 1 To 0 Step -1
                _returnValue.Add(_functionsSortedDictionary.ElementAt(_counter).Key, _functionsSortedDictionary.ElementAt(_counter).Value)
            Next _counter

            Return _returnValue
        End Function

        ''' <summary>Gets the operators made available by the compiler.</summary>
        Public Shared ReadOnly Property SupportedOperatorsTypes() As Sys.Type()
            Get
                Dim _returnValue() As Sys.Type = Ope.Operator.___OperatorsSortedGet().Values.ToArray()

                Return _returnValue
            End Get
        End Property

        ''' <summary>Gets the values types made available by the compiler.</summary>
        Public Shared ReadOnly Property SupportedValuesTypes() As Sys.Type()
            Get
                Dim _returnValue() As Sys.Type = Val.Value.___TypesGet()

                Return _returnValue
            End Get
        End Property

        ''' <summary>Gets the method used by the compiler in string comparison procedures.</summary>
        Public Shared ReadOnly Property TextComparisonMethod() As StringComparison
            Get
                Dim _returnValue As StringComparison = ___Texts.ComparisonMethod

                Return ___Texts.ComparisonMethod
            End Get
        End Property

        Private __FunctionsAndOperatorsAndBooleanValuesSorted As Gen.Dictionary(Of System.String, ___FunctionsAndOperatorsAndBooleanValuesItem) = Nothing
        Friend Function ___FunctionsAndOperatorsAndBooleanValuesSortedGet() As Gen.Dictionary(Of System.String, ___FunctionsAndOperatorsAndBooleanValuesItem)
            If __FunctionsAndOperatorsAndBooleanValuesSorted.IsNull("__FunctionsAndOperatorsAndBooleanValuesSorted", False) Then
                Dim _itemsDictionary As Gen.SortedDictionary(Of System.String, ___FunctionsAndOperatorsAndBooleanValuesItem) = New Gen.SortedDictionary(Of System.String, ___FunctionsAndOperatorsAndBooleanValuesItem)()
                For Each _function As Fun.Function In ___FunctionsSortedGet().Values.ToArray()
                    _itemsDictionary.Add(_function.Name, New ___FunctionsAndOperatorsAndBooleanValuesItem(___FunctionsAndOperatorsAndBooleanValuesItemType.Function, _function))
                Next _function
                For Each _operator As Gen.KeyValuePair(Of System.String, Sys.Type) In Ope.Operator.___OperatorsSortedGet()
                    _itemsDictionary.Add(_operator.Key, New ___FunctionsAndOperatorsAndBooleanValuesItem(___FunctionsAndOperatorsAndBooleanValuesItemType.Operator, _operator.Value))
                Next _operator
                _itemsDictionary.Add(Val.Boolean.FalseValueRepresentation, New ___FunctionsAndOperatorsAndBooleanValuesItem(___FunctionsAndOperatorsAndBooleanValuesItemType.BooleanFalseValue, Nothing))
                _itemsDictionary.Add(Val.Boolean.TrueValueRepresentation, New ___FunctionsAndOperatorsAndBooleanValuesItem(___FunctionsAndOperatorsAndBooleanValuesItemType.BooleanTrueValue, Nothing))
                __FunctionsAndOperatorsAndBooleanValuesSorted = New Gen.Dictionary(Of System.String, ___FunctionsAndOperatorsAndBooleanValuesItem)()
                For _counter As Sys.Int32 = _itemsDictionary.Count - 1 To 0 Step -1
                    __FunctionsAndOperatorsAndBooleanValuesSorted.Add(_itemsDictionary.ElementAt(_counter).Key, _itemsDictionary.ElementAt(_counter).Value)
                Next _counter
            End If

            Return __FunctionsAndOperatorsAndBooleanValuesSorted
        End Function

        ''' <summary>Event raised whenever the compiler requires the user to indicate the types of the variables found while compiling expressions.</summary>
        Public Event ExpressionVariablesTypesRequest As EventHandler(Of CompilerExpressionVariablesTypesRequestEventArgs)

        Friend Sub ___OnExpressionVariablesTypesRequest(ByVal _sender As Sys.Object, ByVal _arguments As CompilerExpressionVariablesTypesRequestEventArgs)
            RaiseEvent ExpressionVariablesTypesRequest(_sender, _arguments)
        End Sub

        ''' <summary>Compiles an expression.</summary>
        ''' <param name="_expression">Expression to compile.</param>
        ''' <exception cref="IsNullException">_expression is null.</exception>
        ''' <exception cref="CompilerExpressionIsEmptyException">_expression is empty.</exception>
        ''' <exception cref="CompilerExpressionStringsDelimitersCountIsValidNotException">_expression contains a string that is not delimited.</exception>
        ''' <exception cref="CompilerExpressionStringsAreValidNotException">_expression contains strings that are not valid.</exception>
        ''' <exception cref="CompilerExpressionDateTimesDelimitersCountIsValidNotException">_expression contains a date time that is not delimited.</exception>
        ''' <exception cref="CompilerExpressionDateTimesAreValidNotException">expression contains date times that are not valid.</exception>
        ''' <exception cref="CompilerExpressionLevelsDelimitersCountAreEqualNotException">_expression contains different counts of begin delimiters and end delimiters.</exception>
        ''' <exception cref="CompilerExpressionLevelsDelimitersAreValidNotException">_expression contains levels that are not properly nested.</exception>
        ''' <exception cref="CompilerExpressionHasUndeterminedTokensException">_expression contains parts that can not be parsed.</exception>
        ''' <exception cref="CompilerExpressionHasWhiteSpaceTokensOnlyException">_expression is composed just by white-space.</exception>
        ''' <exception cref="CompilerExpressionVariablesTypesRequestAbortedException">The expression variables types request threw an exception.</exception>
        ''' <exception cref="CompilerExpressionVariableTypeIsValidNotException">_expression contains a variable that did not accept a type change.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionStartsWithFunctionParametersSeparatorException">An _expression subexpression starts with a function parameters separator.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionEndsWithFunctionParametersSeparatorException">An _expression subexpression ends with a function parameters separator.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionContainsAdjacentFunctionParametersSeparatorsException">An _expression subexpression contains adjacent function parameters separators.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionTokenTypeIsValidNotException">An _expression subexpression contains tokens of invalid types.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionContainsAdjacentValuesOrVariablesOrFunctionCallsException">An _expression subexpression contains adjacent value, variable or function call tokens.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionContainsAdjacentOperatorsException">An _expression subexpression contains adjacent operator tokens.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionEndsWithOperatorException">An _expression subexpression ends with an operator separator.</exception>
        ''' <exception cref="CompilerExpressionSubexpressionOperatorHasNoCorrespondingFunctionSignatureException">An _expression subexpression contains an operator for which there is no compatible function signature.</exception>
        ''' <exception cref="CompilerExpressionLevelWhenEmptyMustBePrecededByFunctionNameException">An _expression level is empty and is not preceded by a function name.</exception>
        ''' <exception cref="CompilerExpressionFunctionNameAndParametersHaveNoCorrespondingFunctionSignatureException">An _expression subexpression contains a function name and parameters for which there is no compatible function signature.</exception>
        ''' <exception cref="CompilerExpressionLevelWhenWithTwoOrMoreParametersMustBePrecededByFunctionNameException">An _expression level containing two or more function parameters is not preceded by a function name.</exception>
        ''' <exception cref="CompilerExpressionFunctionParametersAreOnlyAllowedInsideLevelsException">_expression base level contains function parameters.</exception>
        ''' <exception cref="CompilerExpressionProducedNoEndResultException">_expression compilation process resulted in no end token.</exception>
        ''' <exception cref="CompilerExpressionRootTokenTypeIsValidNotException">_expression compilation process resulted in a single token, but of an invalid type.</exception>
        ''' <returns>The end result is an Expression instance that can be used for solving.</returns>
        Public Function Compile(ByVal _expression As System.String) As Expression
            _expression.IsNullNot("_expression", True)

            Dim _tokens As Gen.List(Of Token) = __ExpressionLexicalAnalysis.AssertAndParse(Me, _expression)
            Dim _tokensVariables() As Variable = __ExpressionVariablesTypesRequest.RequestAndAssertAndDefine(Me, _expression, _tokens)
            Dim _returnValue As Expression = __ExpressionSyntacticalAnalysis.AssertAndDetermineExpression(Me, _expression, _tokens, _tokensVariables)

            Return _returnValue
        End Function

    End Class
End Namespace
